<?php
namespace app\api\base;

/**
 * Class RestfulBase
 * API控制器(无视图)抽象基类
 * @package app\common\base
 * @abstract RestfulBase
 * @version 1.0
 * @author ygzhang<ygzhang.cn@msn.com>
 */
use think\Request;
use think\Response;
use think\Config;
use think\Hook;
use think\Loader;
use app\modules\supplier\call\Auth as SupplierAuthCall;
use app\modules\supplier\call\Status as SupplierStatusCall;
use Supplier\Org\Auth as AuthLibrary;
use Wechat\TPWechat;

abstract class RestfulBase
{
	/**
	 * @var \think\Request Request实例
	 */
	protected $request;
	protected $_rest_config = [
		'method_allow' => ['get', 'post', 'put', 'delete'],
		'method_default' => 'get',
		'type_allow' => ['json', 'xml'],
		'type_default' => 'json',
		'http_header' => [
			'Access-Control-Allow-Origin' => '*',
			'Access-Control-Allow-Credentials' => 'true',
			'Access-Control-Allow-Methods' => 'GET,POST,PUT,DELETE',
			'Access-Control-Allow-Headers' => 'Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type,X-E4M-With,Authorization'
		],
		'http_options' => []
	];

	protected $_rest_type = '';

	protected $_open_auth = ['user' => true, 'open_api' => true];
	protected $_is_login  = false;
	protected $_account = null;
	protected $_client_device = 0;

	protected $_api_config = [];//API入口模块Config配置信息

	protected $this_module = 'api';
	protected $this_controller = 'index';
	protected $this_action = 'index';

	//不需要任何权限（也不需要账户登录）的开放接口
    protected $access_public = [
        //模块
        'this_module' => [
            'open_api',
        ],
        //模块+控制器节点
        'this_module_controller' => [
            'api/supplier.login',
            'api/supplier.login_out',
        ],
        //完整MVC节点
        'this_module_controller_action' => [],
        //自定义节点
        'this_note' => []
    ];

//    public $wx_info=array();


	public function __construct(Request $request = null)
	{

		//路由是否注入Request
		if (is_null($request)) {
			$request = Request::instance();
		}
		$this->request = $request;
		
		$this->_intBase();

		//取得当前会话的URL节点数据
		$this->this_module = Loader::parseName($this->request->module());
		$this->this_controller = Loader::parseName($this->request->controller());
		$this->this_action = Loader::parseName($this->request->action());

		//未认证时加载子类方法 根据逻辑去除部分认证
        $this->_initialize();

		if ($this->_open_auth['user']) {
			$this->_authUser();
		}
		if ($this->_open_auth['open_api']) {
			$this->_authOpenApi();
		}
	
		$this->_runningState();
		$this->_traits_initialize();

//		//微信授权
//        $this->options = Config::get('wechat');
//        $this->wechatObj = new TPWechat($this->options);
//        $this->wx_info = $this->get_info();

	}

	/**
	 * API入口组及API子模块运行状态检查与初始化
	 */
	protected final function _runningState()
	{
		// if ($this->_is_login === false) {
		// 	$this->apiErrorSend('用户未登录！');
		// }
		if (!$this->_is_login && $this->_open_auth['user']) {
		    $this->apiErrorSend("用户未登录", -6);
		}
	}

	/**
	 * 子类traits自动初始化
	 */
	protected function _traits_initialize()
	{
	}

	/**
	 * 子类自动初始化
	 */
	protected function _initialize()
	{
	}


	/**
	 * 基类自动初始化
	 */
	final protected function _intBase()
	{
		//todo 权限控制 检测用户是否登录
		$this->_client_device = get_client_device(1);
		if (SupplierStatusCall::isLogin(true)) {
			$this->_is_login = true;
			$this->_account = session('account');
			// $this->_is_admin = UserStatusCall::isAdmin();
			// $this->_is_root = UserStatusCall::isRoot();;
		}

        //首先通过.扩展名方式请求资源格式
		$this->_rest_type = $this->request->ext();
		//当前请求的URL控制器(小写，去除API分组名称)
		$_m_controller = $this->request->controller();

		if (false !== strpos($_m_controller, '.')) {
			$arg = explode('.', $_m_controller);
			$_m_controller = parse_name_style(array_pop($arg), 0);
		}
		//是否手动指定了扩展名来限制请求资源格式
		if ('' == $this->_rest_type || $_m_controller == $this->_rest_type) {
			$this->_rest_type = $this->request->type();
			if (!in_array($this->_rest_type, $this->_rest_config['type_allow'])) {
				//非法或未指定，采用默认(JSON)
				$this->_rest_type = $this->_rest_config['type_default'];
			}
		} elseif ($this->request->isAjax()) {
			//自动确认Ajax请求资源类型
			if (!in_array($this->_rest_type, $this->_rest_config['type_allow'])) {
				//非法或未指定，采用默认(JSON)
				$this->_rest_type = $this->_rest_config['type_default'];
			}
		} else {
			//自动确认其他方式资源类型
			if (!in_array($this->_rest_type, $this->_rest_config['type_allow'])) {
				//使用html/text时强制转换为XML
				if (in_array($this->_rest_type, ['html', 'text'])) {
					$this->_rest_type = 'xml';
				} else {
					//非法或未指定，采用默认(JSON)
					$this->_rest_type = $this->_rest_config['type_default'];
				}
			}
		}
		// 确定请求请求方式
		$this->_rest_method = strtolower($this->request->method());
		if (!in_array($this->_rest_method, $this->_rest_config['method_allow'])) {
			// 请求方式非法 则用默认请求方法
			$this->_rest_method = $this->_rest_config['method_default'];
		}
	}

	/**
	 * Restful 以控制器方法+扩展名指定请求数据格式模式调用接口
	 * @access public
	 * @param string $action 控制器方法名
	 * @return Response
	 * 实例
	 * 客户端以 [ api/user.user_info/get.xml ]  方法+扩展名指定请求数据格式模式调用接口，将调用get方法并以XML格式返回数据
	 */
	public function _empty($action = '')
	{
		$suffix = explode('|', Config::get('url_html_suffix'));
		foreach ($suffix as $_k => $_v) {
			$suffix[$_k] = '.' . $_v;
		}
		$action = strip_tags(trim(strtolower($action)));
		$action = str_replace($suffix, '', $action);
		$result = $this->_beforeEmpty($action);
		if (false === $result) {
			if (method_exists($this, $action)) {
				return call_user_func([$this, $action]);
			} else {
				return $this->apiError("访问的接口不存在", -3);
			}
		} else {
			return $result;
		}
	}

	/**
	 * Restful 空操作前置方法
	 * @access protected
	 * @param string $action 控制器方法名
	 * @return false|Response 实例
	 *
	 */
	protected function _beforeEmpty($action = '')
	{
		return false;
	}




	/**
	 * 获取客户端请求参数
	 * 格式及使用方法参照 TP5 request->param()
	 *  API 接口使用授权机制（后期开发计划将进行OpenAPI=>API的映射），不同机制下会对参数进行签名与加密，通过此方法可以正常获取解密后的参数
	 * @param  string|array $name 变量名
	 * @param  mixed $default 默认值
	 * @param string|array $filter 过滤方法
	 * @return  mixed
	 */
	protected final function getParams($name='', $default = null, $filter = '')
	{
		return $this->request->param($name, $default, $filter);
	}

	/**
	 * API调用成功，并将响应对象返回给子类控制器
	 * @param  number|string|array $data 返回数据
	 * @param string $msg 提示消息
	 * @param int $code 提示码
	 * @param int $status Http状态发送编码
	 * @return  Response
	 */
	protected final function apiSuccess($data = [], $msg = '', $code = 1, $status = 200)
	{
		return $this->response($data, $code, $msg, $status);
	}

	/**
	 * API调用成功，并将响应对象直接输出且终止运行
	 * @param number|string|array $data 返回数据
	 * @param string $msg 提示消息
	 * @param int $code 提示码
	 * @param int $status Http状态发送编码
	 * @return  Response
	 */
	protected final function apiSuccessSend($data = [], $msg = '', $code = 1, $status = 200)
	{
		$response = $this->response($data, $code, $msg, $status);
		$response->send();
	}

	/**
	 * API调用失败，并将响应对象返回给子类控制器
	 * @param string $msg 提示消息（可选）
	 * @param int $code 提示码
	 * @param int $status Http状态发送编码
	 * @return  Response
	 */
	protected final function apiError($msg = '', $code = 0, $status = 200)
	{
		return $this->response([], $code, $msg, $status);
	}

	/**
	 * API调用失败，并将响应对象直接输出且终止运行
	 * @param string $msg 提示消息（可选）
	 * @param int $code 提示码
	 * @param int $status Http状态发送编码
	 * @return  void
	 */
	protected final function apiErrorSend($msg = '', $code = 0, $status = 200)
	{
		$response = $this->response([], $code, $msg, $status);
		$response->send();
		exit();
	}

	/**
	 * 获得相应输出数据对象
	 * @param number|string|array $data 返回数据
	 * @param int $code 提示码
	 * @param string $msg 提示消息
	 * @param int $status Http状态发送编码
	 * @param array $header Header输出参数
	 * @param array $options 其他输出选项
	 * @return  Response
	 */
	protected final function response($data = [], $code = 1, $msg = '', $status = 200, $header = [], $options = [])
	{
		$code = intval($code);
		$status = intval($status);
		$data = $this->_dataEncode($data, $code, $msg);
		if (!is_array($header)) {
			$header = [];
		}
		$header = array_merge($this->_rest_config['http_header'], $header);

		if (!is_array($options)) {
			$options = [];
		}
		$options = array_merge($this->_rest_config['http_options'], $options);

		if ($this->_rest_type == 'xml') {
			$options['root_node'] = 'api';
		}

		return Response::create($data, $this->_rest_type, $status, $header, $options)->contentType($this->_getContentType($this->_rest_type));
	}

	/**
	 * 组装待返回数据
	 * @param number|string|array $data
	 * @param int $code 提示码
	 * @param string $msg 提示消息
	 * @return array
	 */
	protected final function _dataEncode($data = [], $code, $msg)
	{
		if (!is_string($data) && !is_numeric($data) && !is_array($data)) {
			if (is_bool($data)) {
				$data = $data ? 1 : 0;
			} else {
				$data = [];
			}
		}
		$code = intval($code);
		if (is_array($msg)) {
			$msg = implode(';', $msg);
		}
		if (!is_string($msg) || empty($msg)) {
			if ($code < 1) {
				$msg = "Error";
			} else {
				$code = 1;
				$msg = 'Success';
			}
		}
		$encode = ['code' => $code, 'msg' => $msg, 'data' => $data];
		// if($this->_is_login && $this->_account['has_msg']){
		// 	$encode['has_msg']=$this->_account['has_msg'];
		// }
		// if($this->_is_login && $this->_account['has_task']){
		// 	$encode['has_task']=$this->_account['has_task'];
		// }
		// if ((isset($this->_api_config['debug_state']) && $this->_api_config['debug_state']) || (isset($this->_module_config['debug_state']) && $this->_module_config['debug_state'])) {
		// 	$encode['debug'] = [
		// 		'module_id' => $this->_module_id,
		// 		'module_config' => $this->_module_config,
		// 		'debug_msg' => $this->_debug_msg,
		// 	];
		// }
		return $encode;
	}

		/**
	 * 认证会话用户是否已登录且是否具有访问API节点权限(RBAC)
	 * 用于必须已登录用户的API交互接口
	 * @access protected
	 * @return void
	 */
	final protected function _authUser()
	{
	
        if (in_array(strtolower($this->this_module . '@' . $this->this_controller . '@' . $this->this_action), $this->access_public['this_note'])) {
            return;
        } else if (in_array(strtolower($this->this_module . '/' . $this->this_controller . '/' . $this->this_action), $this->access_public['this_module_controller_action'])) {
            return;
        } else if (in_array(strtolower($this->this_module . '/' . $this->this_controller), $this->access_public['this_module_controller'])) {
            return;
        } else if (in_array(strtolower($this->this_module), $this->access_public['this_module'])) {
            return;
        }
		if (!$this->_is_login) {
			$this->apiErrorSend("用户未登录", -6);
		}
		
		$auth = new AuthLibrary();

		 // //如果是超级管理员，不进行权限认证
        if ($this->_account['is_super']) {
        	return true;
        }
        
	    $auth = new AuthLibrary();
		if(!$auth->check(strtolower($this->this_module . '/' . $this->this_controller . '/' . $this->this_action),$this->_account['supper_user_id'])){
			$this->apiErrorSend("没有权限",-7);
		}
	}
	
	/**
	 * Oauth2认证: 远程会话用户终端是否具备API访问权限(Oauth2)
	 * 通常用户离岸应用(APP或CURL请求)的API交互接口
	 * API 接口使用授权机制（后期开发计划将进行OpenAPI=>API的映射）
	 * @access protected
	 * @return void
	 */
	protected final function _authOpenApi()
	{
	}

	/**
	 * Oauth2认证: 特殊操作下必须要验证用户AccessToken以识别用户是否登陆
	 * 比如读取用户信息，上传头像，注销登录
	 * 比如读取用户信息，上传头像，注销登录
	 * @param bool $force_response
	 */
	protected final function _checkAccessToken($force_response = true)
	{
	}

	/**
	 * 取得相应文件头ContentType
	 * 用途：如不指定，TP5运行机制会自动依据请求来源，输出是控制文件头ContentType，这里按照API接口规范要求获取
	 * @param string $type
	 * @return mixed|string
	 */
	protected final function _getContentType($type = 'json')
	{
		$type_list = array(
			'html' => 'text/html',
			'xml' => 'application/xml',
			'json' => 'application/json'
		);
		return isset($type_list[$type]) ? $type_list[$type] : 'text/html';
	}

	//微信授权

//    //获取用户信息
//    protected function get_info()
//    {
//        $access=cookie('access_token');
//        $refresh=cookie('refresh_token');
//        $openid=cookie('myopenid');
//
//        if($access)
//        {
//
//            return $this->wechatObj->getOauthUserinfo($access,$openid);
//
//        }else if($refresh)
//        {
//
//            $token=$this->wechatObj->getOauthRefreshToken($refresh);
//            if($token)
//            {
//                cookie('access_token',$token['access_token'],C('ACCESS_TOKEN_TIME'));
//                cookie('refresh_token',$token['access_token'],C('REFRESH_TOKEN_TIME'));
//                cookie('myopenid',$token['openid'],C('REFRESH_TOKEN_TIME'));
//                return $this->wechatObj->getOauthUserinfo($token['access_token'],$token['openid']);
//            }else
//            {
//                cookie('access_token',null);
//                cookie('refresh_token',null);
//                $this->get_oauth(1);
//            }
//        }else
//        {
//
//            $code=isset($_GET['code'])?$_GET['code']:'';
//            if($code)
//            {
//                $token=$this->wechatObj->getOauthAccessToken();
//                $info=$this->wechatObj->getOauthUserinfo($token['access_token'],$token['openid']);
//                if(!$info){$this->error('授权失败！！请重新授权');}
//                cookie('access_token',$token['access_token'],C('ACCESS_TOKEN_TIME'));
//                cookie('refresh_token',$token['access_token'],C('REFRESH_TOKEN_TIME'));
//                cookie('myopenid',$token['openid'],C('REFRESH_TOKEN_TIME'));
//                return $info;
//            }else
//            {
//                $this->get_oauth(1);
//            }
//        }
//    }
//
//    //获取授权--0:基本信息授权,1:所有信息授权
//    protected function get_oauth($all=0)
//    {
//        $url=$this->get_url();
//        $type=$all==1?'snsapi_userinfo':'snsapi_base';
//        $wurl=$this->wechatObj->getOauthRedirect($url,$type,$type);
//        redirect($wurl);
//    }
//
//    //获取当前地址
//    public function get_url()
//    {
//        //协议
//        $protocol=isset($_SERVER['SERVER_PORT'])&&$_SERVER['SERVER_PORT']=='443'?'https://':'http://';
//        //入口
//        $main=$_SERVER['PHP_SELF']?$_SERVER['PHP_SELF']:$_SERVER['SCRIPT_NAME'];
//        //参数
//        $parm=isset($_SERVER['PATH_INFO'])?$_SERVER['PATH_INFO']:'';
//        //地址
//        $url=isset($_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI']:$main.(isset($_SERVER['QUERY_STRING'])?'?'.$_SERVER['QUERY_STRING']:$parm);
//        //返回地址
//        return $protocol.(isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:'').$url;
//    }
}